/*
 * Copyright (C) 2011 ViewlinkDC++ Team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"
#include <commctrl.h>
#include "UpdateDlg.h"
#include "Resource.h"
#include "fuSearch.h"
#include "viewUpdateLang.h"



#define _DISABLE_CANCEL_BUTTON_
#define _TRY_TO_COPY_BEFORE_ERROR_ 5

UpdateDlg* UpdateDlg::_currentInstance = 0;

UpdateDlg::~UpdateDlg()
{
	if( NULL != _updateThread )
	{
		_isCancel = true;
		::WaitForSingleObject( _updateThread , 1000 );
		::CloseHandle( _updateThread );
		_updateThread = NULL;
	}
}


LRESULT UpdateDlg::DoModal(HWND parent)
{
	INITCOMMONCONTROLSEX initControls;
	::ZeroMemory(&initControls, sizeof(initControls));
	initControls.dwSize = sizeof(initControls);
	initControls.dwICC = ICC_PROGRESS_CLASS;
	if (InitCommonControlsEx(&initControls) == FALSE)
	{
		_RPT1(_CRT_WARN, "InitCommonControlsEx failed with: %d\n", ::GetLastError());
		return -1;
	}

	_ASSERT(_currentInstance == 0);
	_currentInstance = this;	
	INT_PTR result = ::DialogBox(NULL/*::GetModuleHandle(0)*/, MAKEINTRESOURCE(IDD), parent, ViewUpdateDialogProc);
	_currentInstance = 0;

	return (int)result;
}


INT_PTR CALLBACK UpdateDlg::ViewUpdateDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	BOOL mesageProccessed = FALSE;

	INITCOMMONCONTROLSEX InitCtrlEx;

	InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
	InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
	InitCommonControlsEx(&InitCtrlEx);

	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		{
			if (!UpdateDlg::_currentInstance->onInitDialog(hDlg))
			{
				EndDialog(hDlg, -1);
			}
			mesageProccessed = TRUE;
			break;
		}

	case WM_COMMAND:
		if (LOWORD(wParam) == IDCANCEL)
		{
			UpdateDlg::_currentInstance->onCloseCmd(message, LOWORD(wParam), hDlg, mesageProccessed);
		}
		break;
	}
	return (INT_PTR)mesageProccessed;
}


LRESULT UpdateDlg::onInitDialog(HWND hDlg)
{
	_hDlg = hDlg;

	SetWindowText(hDlg, LAPP_CAPTION);

	SetDlgItemText(hDlg, IDC_STAGE, LAPP_CHECKING);
	SetDlgItemText(hDlg, IDC_STAGESTEP,L"");

	HWND progressDlg = GetDlgItem(hDlg, IDC_UPDATE_PROGRESS);
	::SendMessage(progressDlg, PBM_SETRANGE32, 0, 100);


	//progress.Attach(GetDlgItem(IDC_UPDATE_PROGRESS));
	//progress.SetRange(0, 100);
	// progress.SetMarquee(true);
	// progress.SetPos(100);
	// progress.StepIt();

	// Start Update procedure
	_isCancel = false;
	_updateThread = CreateThread(NULL, 0, UpdateDialogProc, this, 0, NULL);
	if( NULL == _updateThread )
	{
		EndDialog(_hDlg, 0 );
		return 0;
	}

#if defined(_DISABLE_CANCEL_BUTTON_)
	EnableWindow(GetDlgItem(hDlg,IDCANCEL),FALSE);
#endif

	return 1;
}

LRESULT UpdateDlg::onCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
{
#if defined(_DISABLE_CANCEL_BUTTON_)
	if (wID == IDCANCEL)
		return 0;
#endif
	_isCancel = true;
	EndDialog(_hDlg, wID);
	bHandled = TRUE;
	return 0;
}

DWORD WINAPI UpdateDlg::UpdateDialogProc( LPVOID lpParam )
{
	UpdateDlg* this_ = static_cast <UpdateDlg*>(lpParam);
	if (this_)
	{
		if (this_->_isCancel) return 0;
		FileUpdateSearch search(this_->_updateFolder);
		search.Scan();
		if (this_->_isCancel) return 0;
		// Let's try to update
		const int fileUpdateCount = search.GetFilesCount();
		this_->SetProgressPos(0, fileUpdateCount, NULL, this_->_isDeleteMode == FALSE ? LAPP_UPDATEING : LAPP_REMOVING);
		for (int i = fileUpdateCount-1 ; i>=0 ; i--)
		{
			if (this_->_isCancel) return 0;

			wstring nameRel = search.GetRelativeFilename(i);
			if (nameRel.length())
			{
				// wstring nameSource = search.GetAbsoluteFilename(i);
				wstring nameDest = this_->_viewFolder + L"\\"+nameRel;
				if (this_->_isDeleteMode)
				{
					::DeleteFile(search.GetAbsoluteFilename(i).c_str());
				}else
				{
					if ( search.CopyFileWithFolders(i, nameDest) == 0)
					{
						if (!this_->_isFinalTry)
						{
							// On Error
							this_->_error = GetLastError();
							this_->updateProcFailed();
							return 0;
						}else
						{
							DWORD error = 0;
							bool bContinueTry = true;
							while (bContinueTry)
							{
								for (int j = 0; j < _TRY_TO_COPY_BEFORE_ERROR_; j++)
								{
									error = ERROR_SUCCESS ;
									if ( search.CopyFileWithFolders(i, nameDest) == 0 )
									{
										error = GetLastError();
										::Sleep(1000);
									}else 
										break;
								}
								if (error != ERROR_SUCCESS )
								{
									wchar_t buff[MAX_PATH*3];
									_snwprintf_s(buff, _countof(buff), _countof(buff)-1, LAPP_CANTCOPY_FILE, nameRel.c_str(), nameDest.c_str(), error);
									bContinueTry = MessageBox(this_->_hDlg, buff,  LAPP_CAPTION, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY;		
								}else
									break;
							}
							if (error != ERROR_SUCCESS)
							{
								this_->_error = error;
								this_->updateProcFailed();
								return 0;
							}
						}
					}
				}
				this_->SetProgressPos(fileUpdateCount-i, fileUpdateCount, nameRel.c_str());
			}
		}
		if (this_->_isDeleteMode == TRUE)
		{
			const int folderCounts = search.GetFoldersCount();
			this_->SetProgressPos(0, folderCounts, NULL, LAPP_REMOVED_TEMP_FOLDERS);
			for (int i = folderCounts - 1; i>=0 ; i--)
			{
				wstring nameRel = search.GetRelativeFoldername(i);
				::RemoveDirectory(search.GetAbsoluteFoldername(i).c_str());
				this_->SetProgressPos(folderCounts-i, fileUpdateCount, nameRel.c_str());
			}
		}
		this_->updateProcSuccess();
	}
	return 1;
}

void UpdateDlg::updateProcFailed()
{
	_isCancel = true;
	EndDialog (_hDlg, IDABORT );
}
void UpdateDlg::updateProcSuccess()
{
	_isCancel = true;
	EndDialog ( _hDlg, IDOK );
}

void 
UpdateDlg::SetProgressPos(int iPos, int iMaxPos, LPCTSTR stagePosLabel, LPCTSTR stageLabel)
{
	if (stageLabel)
		SetDlgItemText(_hDlg, IDC_STAGE, stageLabel);
	if (stagePosLabel)
	SetDlgItemText(_hDlg, IDC_STAGESTEP,stagePosLabel);

	HWND progressDlg = GetDlgItem(_hDlg, IDC_UPDATE_PROGRESS);
	if (iPos >= iMaxPos){
		SendMessage(progressDlg, PBM_SETPOS, 100, 0);
		// progress.SetPos(100);
		return;
	}

	int pos =(int)  (iPos*100.0 / iMaxPos);
	SendMessage(progressDlg, PBM_SETPOS, pos, 0);
	// progress.SetPos(pos);

}
